package com.jp.kefu.mybatis;

/**
 * @author lijin
 */
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 abel533@gmail.com
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.ResultMapping;

import java.util.List;
import java.util.Set;

/**
 * 拼常用SQL的工具类
 *
 * @author liuzh
 * @since 2015-11-03 22:40
 */
public class SqlHelper {

    /**
	* 获取表名 - 支持动态表名
	*
	* @param entityClass
	* @param tableName
	* @return
	*/
    public static String getDynamicTableName(Class<?> entityClass, String tableName) {
	   if (IDynamicTableName.class.isAssignableFrom(entityClass)) {
		  StringBuilder sql = new StringBuilder();
		  sql.append("<choose>");
		  sql.append("<when test=\"@tk.mybatis.mapper.util.OGNL@isDynamicParameter(_parameter) and dynamicTableName != null and dynamicTableName != ''\">");
		  sql.append("${dynamicTableName}\n");
		  sql.append("</when>");
		  //不支持指定列的时候查询全部列
		  sql.append("<otherwise>");
		  sql.append(tableName);
		  sql.append("</otherwise>");
		  sql.append("</choose>");
		  return sql.toString();
	   } else {
		  return tableName;
	   }
    }

    /**
	* 获取表名 - 支持动态表名，该方法用于多个入参时，通过parameterName指定入参中实体类的@Param的注解值
	*
	* @param entityClass
	* @param tableName
	* @param parameterName
	* @return
	*/
    public static String getDynamicTableName(Class<?> entityClass, String tableName, String parameterName) {
	   if (IDynamicTableName.class.isAssignableFrom(entityClass)) {
		  if (StringUtils.isNotEmpty(parameterName)) {
			 StringBuilder sql = new StringBuilder();
			 sql.append("<choose>");
			 sql.append("<when test=\"@tk.mybatis.mapper.util.OGNL@isDynamicParameter(" + parameterName + ") and " + parameterName + ".dynamicTableName != null and " + parameterName + ".dynamicTableName != ''\">");
			 sql.append("${" + parameterName + ".dynamicTableName}");
			 sql.append("</when>");
			 //不支持指定列的时候查询全部列
			 sql.append("<otherwise>");
			 sql.append(tableName);
			 sql.append("</otherwise>");
			 sql.append("</choose>");
			 return sql.toString();
		  } else {
			 return getDynamicTableName(entityClass, tableName);
		  }

	   } else {
		  return tableName;
	   }
    }



    /**
	* 判断自动!=null的条件结构
	*
	* @param column
	* @param contents
	* @param empty
	* @return
	*/
    public static String getIfNotNull(EntityColumn column, String contents, boolean empty) {
	   return getIfNotNull(null, column, contents, empty);
    }

    /**
	* 判断自动==null的条件结构
	*
	* @param column
	* @param contents
	* @param empty
	* @return
	*/
    public static String getIfIsNull(EntityColumn column, String contents, boolean empty) {
	   return getIfIsNull(null, column, contents, empty);
    }

    /**
	* 判断自动!=null的条件结构
	*
	* @param entityName
	* @param column
	* @param contents
	* @param empty
	* @return
	*/
    public static String getIfNotNull(String entityName, String column, String contents, boolean empty) {
	   StringBuilder sql = new StringBuilder();
	   sql.append("<if test=\"");
	   if (StringUtils.isNotEmpty(entityName)) {
		  sql.append(entityName).append(".");
	   }
	   sql.append(column).append(" != null");
	   /*if (empty && column.getJavaType().equals(String.class)) {
		  sql.append(" and ");
		  if (StringUtils.isNotEmpty(entityName)) {
			 sql.append(entityName).append(".");
		  }
		  sql.append(column.getProperty()).append(" != '' ");
	   }*/
	   sql.append("\">");
	   sql.append(contents);
	   sql.append("</if>");
	   return sql.toString();
    }

    /**
	* 判断自动==null的条件结构
	*
	* @param entityName
	* @param column
	* @param contents
	* @param empty
	* @return
	*/
    public static String getIfIsNull(String entityName, String column, String contents, boolean empty) {
	   StringBuilder sql = new StringBuilder();
	   sql.append("<if test=\"");
	   if (StringUtils.isNotEmpty(entityName)) {
		  sql.append(entityName).append(".");
	   }
	   sql.append(column).append(" == null");
	   /*if (empty && column.getJavaType().equals(String.class)) {
		  sql.append(" or ");
		  if (StringUtils.isNotEmpty(entityName)) {
			 sql.append(entityName).append(".");
		  }
		  sql.append(column.getProperty()).append(" == '' ");
	   }*/
	   sql.append("\">");
	   sql.append(contents);
	   sql.append("</if>");
	   return sql.toString();
    }

    /**
	* 获取所有查询列，如id,name,code...
	*
	* @param entityClass
	* @return
	*/
    public static String getAllColumns(ResultMap resultMap) {
	   List<ResultMapping> resultMappings = resultMap.getResultMappings();
	   StringBuilder sql = new StringBuilder();
	   for (ResultMapping resultMapping : resultMappings) {

		  sql.append(resultMapping.getColumn()).append(",");
	   }
	   return sql.substring(0, sql.length() - 1);
    }

    /**
	* select xxx,xxx...
	*
	* @param entityClass
	* @return
	*/
    public static String selectAllColumns(ResultMap resultMap) {
	   StringBuilder sql = new StringBuilder();
	   sql.append("SELECT ");
	   sql.append(getAllColumns(resultMap));
	   sql.append(" ");
	   return sql.toString();
    }

    /**
	* select count(x)
	*
	* @param entityClass
	* @return
	*/
    public static String selectCount(Class<?> entityClass) {
	   StringBuilder sql = new StringBuilder();
	   sql.append("SELECT ");
		  sql.append("COUNT(*) ");
	   return sql.toString();
    }

    /**
	* select case when count(x) > 0 then 1 else 0 end
	*
	* @param entityClass
	* @return
	*/
    public static String selectCountExists(Class<?> entityClass) {
	   StringBuilder sql = new StringBuilder();
	   sql.append("SELECT CASE WHEN ");
		  sql.append("COUNT(*) ");
	   sql.append(" > 0 THEN 1 ELSE 0 END AS result ");
	   return sql.toString();
    }

    /**
	* from tableName - 动态表名
	*
	* @param entityClass
	* @param defaultTableName
	* @return
	*/
    public static String fromTable(Class<?> entityClass, String defaultTableName) {
	   StringBuilder sql = new StringBuilder();
	   sql.append(" FROM ");
	   sql.append(getDynamicTableName(entityClass, defaultTableName));
	   sql.append(" ");
	   return sql.toString();
    }

    /**
	* update tableName - 动态表名
	*
	* @param entityClass
	* @param defaultTableName
	* @return
	*/
    public static String updateTable(Class<?> entityClass, String defaultTableName) {
	   return updateTable(entityClass, defaultTableName, null);
    }

    /**
	* update tableName - 动态表名
	*
	* @param entityClass
	* @param defaultTableName 默认表名
	* @param entityName       别名
	* @return
	*/
    public static String updateTable(Class<?> entityClass, String defaultTableName, String entityName) {
	   StringBuilder sql = new StringBuilder();
	   sql.append("UPDATE ");
	   sql.append(getDynamicTableName(entityClass, defaultTableName, entityName));
	   sql.append(" ");
	   return sql.toString();
    }

    /**
	* delete tableName - 动态表名
	*
	* @param entityClass
	* @param defaultTableName
	* @return
	*/
    public static String deleteFromTable(Class<?> entityClass, String defaultTableName) {
	   StringBuilder sql = new StringBuilder();
	   sql.append("DELETE FROM ");
	   sql.append(getDynamicTableName(entityClass, defaultTableName));
	   sql.append(" ");
	   return sql.toString();
    }

    /**
	* insert into tableName - 动态表名
	*
	* @param entityClass
	* @param defaultTableName
	* @return
	*/
    public static String insertIntoTable(Class<?> entityClass, String defaultTableName) {
	   StringBuilder sql = new StringBuilder();
	   sql.append("INSERT INTO ");
	   sql.append(getDynamicTableName(entityClass, defaultTableName));
	   sql.append(" ");
	   return sql.toString();
    }

    /**
	* insert table()列
	*
	* @param entityClass
	* @param skipId      是否从列中忽略id类型
	* @param notNull     是否判断!=null
	* @param notEmpty    是否判断String类型!=''
	* @return
	*/
    public static String insertColumns(ResultMap resultMap, boolean skipId, boolean notNull, boolean notEmpty) {
	   StringBuilder sql = new StringBuilder();
	   sql.append("<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">");
	   //获取全部列
	   List<ResultMapping> resultMappings = resultMap.getResultMappings();
	   for (ResultMapping resultMapping : resultMappings) {
		  String column = resultMapping.getColumn();
		  //当某个列有主键策略时，不需要考虑他的属性是否为空，因为如果为空，一定会根据主键策略给他生成一个值
		  if (skipId && column.equalsIgnoreCase("id")) {
			 continue;
		  }
		  if (notNull) {
			 sql.append(SqlHelper.getIfNotNull(column, column + ",", notEmpty));
		  } else {
			 sql.append(column + ",");
		  }
	   }
	   sql.append("</trim>");
	   return sql.toString();
    }

    /**
	* insert-values()列
	*
	* @param entityClass
	* @param skipId      是否从列中忽略id类型
	* @param notNull     是否判断!=null
	* @param notEmpty    是否判断String类型!=''
	* @return
	*/
    public static String insertValuesColumns(ResultMap resultMap, boolean skipId, boolean notNull, boolean notEmpty) {
	   StringBuilder sql = new StringBuilder();
	   sql.append("<trim prefix=\"VALUES (\" suffix=\")\" suffixOverrides=\",\">");
	   //获取全部列
	   Set<EntityColumn> columnList = EntityHelper.getColumns(entityClass);
	   //当某个列有主键策略时，不需要考虑他的属性是否为空，因为如果为空，一定会根据主键策略给他生成一个值
	   for (EntityColumn column : columnList) {
		  if (!column.isInsertable()) {
			 continue;
		  }
		  if (skipId && column.isId()) {
			 continue;
		  }
		  if (notNull) {
			 sql.append(SqlHelper.getIfNotNull(column, column.getColumnHolder() + ",", notEmpty));
		  } else {
			 sql.append(column.getColumnHolder() + ",");
		  }
	   }
	   sql.append("</trim>");
	   return sql.toString();
    }

    /**
	* update set列
	*
	* @param entityClass
	* @param entityName  实体映射名
	* @param notNull     是否判断!=null
	* @param notEmpty    是否判断String类型!=''
	* @return
	*/
    public static String updateSetColumns(Class<?> entityClass, String entityName, boolean notNull, boolean notEmpty) {
	   StringBuilder sql = new StringBuilder();
	   sql.append("<set>");
	   //获取全部列
	   Set<EntityColumn> columnList = EntityHelper.getColumns(entityClass);
	   //当某个列有主键策略时，不需要考虑他的属性是否为空，因为如果为空，一定会根据主键策略给他生成一个值
	   for (EntityColumn column : columnList) {
		  if (!column.isId() && column.isUpdatable()) {
			 if (notNull) {
				sql.append(SqlHelper.getIfNotNull(entityName, column, column.getColumnEqualsHolder(entityName) + ",", notEmpty));
			 } else {
				sql.append(column.getColumnEqualsHolder(entityName) + ",");
			 }
		  }
	   }
	   sql.append("</set>");
	   return sql.toString();
    }

    /**
	* where主键条件
	*
	* @param entityClass
	* @return
	*/
    public static String wherePKColumns(Class<?> entityClass) {
	   StringBuilder sql = new StringBuilder();
	   sql.append("<where>");
	   //获取全部列
	   Set<EntityColumn> columnList = EntityHelper.getPKColumns(entityClass);
	   //当某个列有主键策略时，不需要考虑他的属性是否为空，因为如果为空，一定会根据主键策略给他生成一个值
	   for (EntityColumn column : columnList) {
		  sql.append(" AND " + column.getColumnEqualsHolder());
	   }
	   sql.append("</where>");
	   return sql.toString();
    }

    /**
	* where所有列的条件，会判断是否!=null
	*
	* @param entityClass
	* @return
	*/
    public static String whereAllIfColumns(Class<?> entityClass, boolean empty) {
	   StringBuilder sql = new StringBuilder();
	   sql.append("<where>");
	   //获取全部列
	   Set<EntityColumn> columnList = EntityHelper.getColumns(entityClass);
	   //当某个列有主键策略时，不需要考虑他的属性是否为空，因为如果为空，一定会根据主键策略给他生成一个值
	   for (EntityColumn column : columnList) {
		  sql.append(getIfNotNull(column, " AND " + column.getColumnEqualsHolder(), empty));
	   }
	   sql.append("</where>");
	   return sql.toString();
    }

    /**
	* 获取默认的orderBy，通过注解设置的
	*
	* @param entityClass
	* @return
	*/
    public static String orderByDefault(Class<?> entityClass) {
	   StringBuilder sql = new StringBuilder();
	   String orderByClause = EntityHelper.getOrderByClause(entityClass);
	   if (orderByClause.length() > 0) {
		  sql.append(" ORDER BY ");
		  sql.append(orderByClause);
	   }
	   return sql.toString();
    }

    /**
	* example支持查询指定列时
	*
	* @return
	*/
    public static String exampleSelectColumns(ResultMap resultMap) {
	   StringBuilder sql = new StringBuilder();
	   sql.append("<choose>");
	   sql.append("<when test=\"@tk.mybatis.mapper.util.OGNL@hasSelectColumns(_parameter)\">");
	   sql.append("<foreach collection=\"_parameter.selectColumns\" item=\"selectColumn\" separator=\",\">");
	   sql.append("${selectColumn}");
	   sql.append("</foreach>");
	   sql.append("</when>");
	   //不支持指定列的时候查询全部列
	   sql.append("<otherwise>");
	   sql.append(getAllColumns(resultMap));
	   sql.append("</otherwise>");
	   sql.append("</choose>");
	   return sql.toString();
    }

    /**
	* example支持查询指定列时
	*
	* @return
	*/
    public static String exampleCountColumn(ResultMap resultMap) {
	   StringBuilder sql = new StringBuilder();
	   sql.append("<choose>");
	   sql.append("<when test=\"@tk.mybatis.mapper.util.OGNL@hasCountColumn(_parameter)\">");
	   sql.append("COUNT(${countColumn})");
	   sql.append("</when>");
	   sql.append("<otherwise>");
	   sql.append("COUNT(0)");
	   sql.append("</otherwise>");
	   sql.append("</choose>");
	   //不支持指定列的时候查询全部列
	   sql.append("<if test=\"@tk.mybatis.mapper.util.OGNL@hasNoSelectColumns(_parameter)\">");
	   sql.append(getAllColumns(resultMap));
	   sql.append("</if>");
	   return sql.toString();
    }

    /**
	* example查询中的orderBy条件，会判断默认orderBy
	*
	* @return
	*/
    public static String exampleOrderBy() {
	   StringBuilder sql = new StringBuilder();
	   sql.append("<if test=\"orderByClause != null\">");
	   sql.append("order by ${orderByClause}");
	   sql.append("</if>");
	   return sql.toString();
    }

    /**
	* example 支持 for update
	*
	* @return
	*/
    public static String exampleForUpdate() {
	   StringBuilder sql = new StringBuilder();
	   sql.append("<if test=\"@tk.mybatis.mapper.util.OGNL@hasForUpdate(_parameter)\">");
	   sql.append("FOR UPDATE");
	   sql.append("</if>");
	   return sql.toString();
    }

    /**
	* example 支持 for update
	*
	* @return
	*/
    public static String exampleCheck(Class<?> entityClass) {
	   StringBuilder sql = new StringBuilder();
	   sql.append("<bind name=\"checkExampleEntityClass\" value=\"@tk.mybatis.mapper.util.OGNL@checkExampleEntityClass(_parameter, '");
	   sql.append(entityClass.getCanonicalName());
	   sql.append("')\"/>");
	   return sql.toString();
    }

    /**
	* Example查询中的where结构，用于只有一个Example参数时
	*
	* @return
	*/
    public static String exampleWhereClause() {
	   return "<if test=\"_parameter != null\">" +
			 "<where>\n" +
			 "  <foreach collection=\"oredCriteria\" item=\"criteria\">\n" +
			 "    <if test=\"criteria.valid\">\n" +
			 "      ${@tk.mybatis.mapper.util.OGNL@andOr(criteria)}" +
			 "      <trim prefix=\"(\" prefixOverrides=\"and |or \" suffix=\")\">\n" +
			 "        <foreach collection=\"criteria.criteria\" item=\"criterion\">\n" +
			 "          <choose>\n" +
			 "            <when test=\"criterion.noValue\">\n" +
			 "              ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ${criterion.condition}\n" +
			 "            </when>\n" +
			 "            <when test=\"criterion.singleValue\">\n" +
			 "              ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ${criterion.condition} #{criterion.value}\n" +
			 "            </when>\n" +
			 "            <when test=\"criterion.betweenValue\">\n" +
			 "              ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ${criterion.condition} #{criterion.value} and #{criterion.secondValue}\n" +
			 "            </when>\n" +
			 "            <when test=\"criterion.listValue\">\n" +
			 "              ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ${criterion.condition}\n" +
			 "              <foreach close=\")\" collection=\"criterion.value\" item=\"listItem\" open=\"(\" separator=\",\">\n" +
			 "                #{listItem}\n" +
			 "              </foreach>\n" +
			 "            </when>\n" +
			 "          </choose>\n" +
			 "        </foreach>\n" +
			 "      </trim>\n" +
			 "    </if>\n" +
			 "  </foreach>\n" +
			 "</where>" +
			 "</if>";
    }

    /**
	* Example-Update中的where结构，用于多个参数时，Example带@Param("example")注解时
	*
	* @return
	*/
    public static String updateByExampleWhereClause() {
	   return "<where>\n" +
			 "  <foreach collection=\"example.oredCriteria\" item=\"criteria\">\n" +
			 "    <if test=\"criteria.valid\">\n" +
			 "      ${@tk.mybatis.mapper.util.OGNL@andOr(criteria)}" +
			 "      <trim prefix=\"(\" prefixOverrides=\"and |or \" suffix=\")\">\n" +
			 "        <foreach collection=\"criteria.criteria\" item=\"criterion\">\n" +
			 "          <choose>\n" +
			 "            <when test=\"criterion.noValue\">\n" +
			 "              ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ${criterion.condition}\n" +
			 "            </when>\n" +
			 "            <when test=\"criterion.singleValue\">\n" +
			 "              ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ${criterion.condition} #{criterion.value}\n" +
			 "            </when>\n" +
			 "            <when test=\"criterion.betweenValue\">\n" +
			 "              ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ${criterion.condition} #{criterion.value} and #{criterion.secondValue}\n" +
			 "            </when>\n" +
			 "            <when test=\"criterion.listValue\">\n" +
			 "              ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ${criterion.condition}\n" +
			 "              <foreach close=\")\" collection=\"criterion.value\" item=\"listItem\" open=\"(\" separator=\",\">\n" +
			 "                #{listItem}\n" +
			 "              </foreach>\n" +
			 "            </when>\n" +
			 "          </choose>\n" +
			 "        </foreach>\n" +
			 "      </trim>\n" +
			 "    </if>\n" +
			 "  </foreach>\n" +
			 "</where>";
    }

}

